/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  ProtoAdaptor.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2019/01/17
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#include "include/ProtoAdaptor.h"

#include "include/message.h"

namespace qb {
namespace net {

// ProtoList::ProtoList(const List* list) :m_list(list)
//{

//}

ProtoList::ProtoList() : m_list(nullptr) {}

ProtoList::ProtoList(const Variant& v) { v.RefList(m_list); }

bool ProtoList::RefListFrom(const Message& msg, const char* name) {
  return msg.RefList(name, m_list) && m_list;
}

ProtoList::const_iterator ProtoList::begin() const {
  return m_list ? m_list->begin() : const_iterator();
}

ProtoList::const_iterator ProtoList::end() const {
  return m_list ? m_list->end() : const_iterator();
}

////////////////////////////////////////////////////////////////////////////////////
ProtoMap::ProtoMap() : m_map(nullptr) {}

// ProtoMap::ProtoMap(const VariantMap* map) : m_map(map)
//{

//}

ProtoMap::ProtoMap(const Variant& v) { v.RefMap(m_map); }

ProtoMap::~ProtoMap() {
  for (std::vector<ProtoList*>::iterator it = m_reflist.begin();
       it != m_reflist.end(); ++it) {
    if (*it) delete (*it);
  }
  m_reflist.clear();
}

bool ProtoMap::RefList(const char* name, const ProtoList*& pList) const {
  const qb::proto::VariantList* vlist = nullptr;
  if (m_map->RefList(name, vlist) && vlist) {
    ProtoList* temp = new ProtoList(vlist);
    (const_cast<ProtoMap*>(this))->m_reflist.push_back(temp);
    pList = temp;
    return true;
  }
  return false;
}

bool ProtoMap::RefMapFrom(const Message& msg, const char* name) {
  m_map = nullptr;
  return msg.RefMap(name, m_map) && m_map != nullptr;
}

bool ProtoMap::GetList(const char* name, ProtoList& list) const {
  const List* sublist = nullptr;
  m_map->RefList(name, sublist);
  list.m_list = sublist;
  return sublist != nullptr;
}
bool ProtoMap::getList(const char* name, ProtoList& list) const {
  return GetList(name, list);
}

bool ProtoMap::GetMap(const char* name, ProtoMap& map) const {
  const VariantMap* submap = nullptr;
  m_map->RefMap(name, submap);
  map.m_map = submap;
  return submap != nullptr;
}
ProtoMap::operator const ProtoMap::VariantMap&() const { return *m_map; }
bool ProtoMap::RefString(const char* name, const char*& value,
                         size_t& size) const {
  if (m_map && name) return m_map->RefString(name, value, size);
  return false;
}
/////////////////////////////////////////////////////////////////////////////

ProtoMessage::ProtoMessage(const Message& msg)
    : m_message(&msg), m_map(nullptr) {}

ProtoMessage::ProtoMessage(const ProtoMap& map)
    : m_map(map.m_map), m_message(nullptr) {}

bool ProtoMessage::GetList(const char* name, ProtoList& list) const {
  if (m_message == nullptr && m_map == nullptr) return false;
  if (m_message) return list.RefListFrom(*m_message, name);
  const qb::proto::Message::List* sublist = nullptr;
  m_map->RefList(name, sublist);
  list.SetList(sublist);
  return sublist != nullptr;
}

const ProtoMap& ProtoMessage::MapValue() {
  const ProtoMap* m = new ProtoMap(m_message->MapValue());
  return *m;
}
bool ProtoMessage::GetRaw(const char* name, Raw& value) const {
  if (m_message == nullptr && m_map == nullptr) return false;
  return m_message ? m_message->GetRaw(name, value)
                   : m_map->GetRaw(name, value);
}
bool ProtoMessage::getList(const char* name, ProtoList& list) const {
  if (m_message == nullptr && m_map == nullptr) return false;
  const qb::proto::Message::List* sublist = nullptr;
  if (m_message) {
    m_message->RefList(name, sublist);
  } else
    m_map->RefList(name, sublist);
  list.SetList(sublist);
  return sublist != nullptr;
}
bool ProtoMessage::GetMap(const char* name, ProtoMap& map) const {
  if (m_message == nullptr && m_map == nullptr) return false;
  const qb::proto::Message::Map* submap = nullptr;
  if (m_message)
    m_message->RefMap(name, submap);
  else
    m_map->RefMap(name, submap);
  map.SetMap(submap);
  return submap != nullptr;
}
bool ProtoMessage::RefString(const char* name, const char*& value,
                             size_t& size) const {
  if (m_message == nullptr && m_map == nullptr) return false;
  if (m_message) return m_message->RefString(name, value, size);
  return m_map->RefString(name, value, size);
}

}  // namespace net
}  // namespace qb

namespace qb {
namespace base {
namespace msg {
class Comparator {
  typedef qb::base::msg::Value Value;
  typedef qb::base::msg::ValueList ValueList;
  typedef qb::base::msg::ValueMap ValueMap;
  typedef ValueList::const_iterator const_iterator;

 public:
  static bool is_equal(const Value& v1, const qb::proto::Variant& v2) {
    // 对比值类型
    if (v1.Type != v2.Type) return false;
    bool equal = false;
    switch (v1.Type) {
      case Value::VAR_BOOL:
        equal = (bool)v1 == (bool)v2;
        break;
      case Value::VAR_INT8:
        equal = (qb::base::msg::int8_t)v1 == (qb::base::msg::int8_t)v2;
        break;
      case Value::VAR_UINT8:
        equal = (qb::base::msg::uint8_t)v1 == (qb::base::msg::uint8_t)v2;
        break;
      case Value::VAR_INT16:
        equal = (qb::base::msg::int16_t)v1 == (qb::base::msg::int16_t)v2;
        break;
      case Value::VAR_UINT16:
        equal = (qb::base::msg::uint16_t)v1 == (qb::base::msg::uint16_t)v2;
        break;
      case Value::VAR_INT32:
        equal = (qb::base::msg::int32_t)v1 == (qb::base::msg::int32_t)v2;
        break;
      case Value::VAR_UINT32:
        equal = (qb::base::msg::uint32_t)v1 == (qb::base::msg::uint32_t)v2;
        break;
      // case Value::VAR_INT64:equal = (qb::base::msg::int64_t)v1 ==
      // (qb::base::msg::int64_t)v2; break; case Value::VAR_UINT64:equal =
      // (qb::base::msg::uint64_t)v1 == (qb::base::msg::uint64_t)v2; break;
      case Value::VAR_FLOAT:
        equal = (float)v1 == (float)v2;
        break;
      case Value::VAR_DOUBLE:
        equal = (double)v1 == (double)v2;
        break;
      case Value::VAR_STRING: {
        std::string s2;
        v2.GetString(s2);
        int len = v1.size;
        if (v1.data && len > 0 && v1.data[len - 1] == 0) len = strlen(v1.data);
        std::string s1 = v1.data ? std::string(v1.data, len) : "";
        int len2 = s2.size();
        if (s2.c_str() && len2 > 0 && s2[len2 - 1] == 0) {
          len2 = strlen(s2.c_str());
          s2 = std::string(s2.c_str(), len2);
        }
        equal = s1 == s2;
      } break;
      case Value::VAR_INT64: {
        int64_t c1 = v1;
        int64_t c2 = v2.Data.i64;
        equal = c1 == c2;
      } break;
      case Value::VAR_UINT64: {
        uint64_t c1 = v1;
        uint64_t c2 = v2.Data.u64;
        equal = c1 == c2;
      } break;
      case Value::VAR_RAW: {
        const void* val = nullptr;
        size_t len = 0;
        v2.RefRaw(val, len);
        if (v1.size == len && memcmp(val, v1.data, len) == 0) equal = true;
      } break;
      default:
        break;
    }
    return equal;
  }
  static bool is_equal(const ValueList& m1, const qb::proto::VariantList& m2) {
    // 对于list,逐个对比
    if (m1.size() != m2.size()) return false;
    qb::proto::VariantList::const_iterator mit = m2.begin();
    for (const_iterator it = m1.begin(); it != m1.end(); ++it, ++mit) {
      const Value& v = *it;
      const qb::proto::Variant& pv = *mit;
      if (v.Type == Value::VAR_MAP) {
        ValueMap vmap(v);
        const qb::proto::VariantMap* map = nullptr;
        if (!pv.RefMap(map) || !is_equal(vmap, *map)) return false;
      } else if (v.Type == Value::VAR_LIST) {
        ValueList vlist(v);
        const qb::proto::VariantList* list = nullptr;
        if (!pv.RefList(list) || !is_equal(vlist, *list)) return false;
      } else {
        if (!is_equal(v, pv)) return false;
      }
    }
    return true;
  }
  static bool is_equal(const ValueMap& m1, const qb::proto::VariantMap& m2) {
    if (m1.size() != m2.size()) return false;
    qb::proto::VariantMap::const_iterator mit = m2.begin();
    for (const_iterator it = m1.begin(); it != m1.end() && mit != m2.end();
         ++it, ++mit) {
      const Value& v = *it;
      std::string key(v.key, v.keylen);
      if (v.Type == Value::VAR_MAP) {
        ValueMap vmap(v);
        const qb::proto::VariantMap* pmap = nullptr;
        if (!m2.RefMap(key.c_str(), pmap) || !pmap) return false;
        if (!is_equal(vmap, *pmap)) return false;
      } else if (v.Type == Value::VAR_LIST) {
        ValueList vlist(v);
        const qb::proto::VariantList* plist = nullptr;
        if (!m2.RefList(key.c_str(), plist) || !plist) return false;
        if (!is_equal(vlist, *plist)) return false;
      } else {
        const qb::proto::Variant* pv = nullptr;
        Value vlast;  // 通过名称访问是找到最后一个
        if (!m1.find(key.c_str(), vlast)) return false;
        if (!m2.getValue(key.c_str(), pv) || !is_equal(vlast, *pv))
          return false;
      }
    }
    return true;
  }
  static bool is_equal(const ProtoMessage& m1, const qb::proto::Message& m2) {
    if (m1.GetType() != m2.GetType()) return false;
    if (m1.GetSubject() != m2.GetSubject()) return false;
    if (m1.GetMessageID() != m2.GetMessageID()) return false;

    return is_equal(m1.MapValue(), m2.MapValue());
  }
};

bool ProtoUtil::CheckMessage(const ProtoMessage& msg, const char* buf,
                             int len) {
  static bool init = false;
  static bool enalbe = false;
  if (!init) {
    init = true;
    //	enalbe =
    //qb::base::cfg::GetConfigBool(qb::base::kBaseConfigCheckProtoMessage);
    ////syy
  }
  if (!enalbe) return true;
  //	qb::base::HighResTime timer;
  qb::proto::Message m2;
  int ret = m2.decode(buf, len);
  //	int last = timer.elapsed().microsec();
  if (ret <= 0) {
    return false;
  }
  bool equal = Comparator::is_equal(msg, m2);
  /*	if (!equal)
          {
                  //将受到的消息写入到本地文件,便于后面测试分析
                  static int msg_index = 0;
                  equal = Comparator::is_equal(msg, m2);
                  char fname[MAX_PATH] = { 0 };
                  SYSTEMTIME st;
                  GetLocalTime(&st);
                  sprintf_s(fname,
     ".\\data\\message\\%s_%s_%02d%02d%02d_%2d%02d%2d_%3d_%d_%d_%d.bin",
                          m2.GetMessageID().c_str(), m2.GetType().c_str(),
                          st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute,
     st.wSecond, st.wMilliseconds, ret, last, msg_index++
                          );
                   qb::base::StringUtil::SaveFileContent(buf, len, fname);
          }*///syy
  return equal;
}
}  // namespace msg
}  // namespace base
}  // namespace qb
